package work.mediway.mdm.biz.service.hierarchy.impl;

import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mediway.hos.app.base.seure.util.SecurityUtils;
import com.mediway.hos.common.utils.ExceptionUtil;
import com.mediway.hos.common.utils.spring.SpringUtils;

import org.springframework.stereotype.Service;

import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import work.mediway.mdm.core.helper.BaseDatabaseHelper;
import work.mediway.mdm.core.page.QueryRequest;
import work.mediway.mdm.biz.mapper.hierarchy.HosOrganizationMapper;
import work.mediway.mdm.biz.service.hierarchy.HosOrganizationService;
import work.mediway.mdm.core.constant.MdmConstant;
import work.mediway.mdm.core.exception.DataException;
import work.mediway.mdm.core.transport.ErrorMsgVO;
import work.mediway.mdm.core.util.DUtil;
import work.mediway.mdm.biz.dto.dict.DictGeneralDTO;
import work.mediway.mdm.biz.entity.hierarchy.HosOrganization;
import work.mediway.mdm.biz.service.common.CommonDictService;

import static work.mediway.mdm.core.enums.TablePrefixEnum.HOS_Organization;

/**
 * <p>
 * 组织机构树 服务实现类
 * </p>
 *
 * @author ruoyi
 * @since 2022-08-25
 */
@SuppressWarnings("unchecked")
@Slf4j
@Service
@RequiredArgsConstructor
public class HosOrganizationServiceImpl extends ServiceImpl<HosOrganizationMapper, HosOrganization> implements HosOrganizationService {

    private final BaseDatabaseHelper baseDatabaseHelper;

    @Override
    public ErrorMsgVO saveHosOrganization(HosOrganization hosOrganization) {
        if (StrUtil.isBlank(hosOrganization.getOrgCode())) {
            hosOrganization.setOrgCode(baseDatabaseHelper.buildCode(HOS_Organization));
        }
        HosOrganization.setCreateHosOrganization(hosOrganization);
        return SpringUtils.getBean(CommonDictService.class).saveDictDetail(DictGeneralDTO.build(hosOrganization, HosOrganization.class), null);
    }

    @Override
    public Object findHosOrganization(HosOrganization hosOrganization, boolean tree, QueryRequest queryRequest) {
        queryRequest.setSize(tree ? Integer.MAX_VALUE : queryRequest.getSize());
        Page<HosOrganization> page = new Page<>(queryRequest.getCurrent(), queryRequest.getSize());
        page.setRecords(this.baseMapper.findHosOrganization(page, hosOrganization));

        return tree ? buildHosOrganizationTree(page.getRecords(), MdmConstant.DEFAULT_ROOT) : page;
    }

    private List<Tree<String>> buildHosOrganizationTree(List<HosOrganization> hosOrganizations, String topNode) {
        List<HosOrganization> organizationSort = hosOrganizations.stream()
                .sorted(Comparator.comparing(HosOrganization::getOrgSeqNo, Comparator.nullsLast(Integer::compareTo))).collect(Collectors.toList());
        List<TreeNode<String>> nodeList = buildHosOrganizationTreeNode(organizationSort);
        //配置
        TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
        // 自定义属性名 都要默认值的
        treeNodeConfig.setWeightKey("order");
        treeNodeConfig.setIdKey("code");
        return TreeUtil.build(nodeList, topNode, treeNodeConfig,
                (treeNode, node) -> {
                    node.setId(treeNode.getId());
                    node.setParentId(treeNode.getParentId());
                    node.setWeight(treeNode.getWeight());
                    node.setName(treeNode.getName());
                    node.putAll(treeNode.getExtra());
                });
    }

    private List<TreeNode<String>> buildHosOrganizationTreeNode(List<HosOrganization> hosOrganizations) {
        List<TreeNode<String>> nodeList = CollUtil.newArrayList();
        hosOrganizations.forEach(item -> {
            TreeNode<String> treeNode = new TreeNode<>(item.getOrgCode(), StrUtil.isBlank(item.getOrgOrgCode()) ? "0" : item.getOrgOrgCode(), item.getOrgDesc(), item.getOrgSeqNo());
            Map<String, Object> extra = MapUtil.newHashMap();
            extra.put("details", item);
            treeNode.setExtra(extra);
            nodeList.add(treeNode);
        });
        return nodeList;
    }


    @Override
    public Object findChildrenHosOrganization(String currentHosOrganizationCode, boolean containsCurrent, boolean tree) {
        List<String> childIdArray = getAllChildrenIdWithParentId(this.list(), currentHosOrganizationCode, tree || containsCurrent);
        if (CollUtil.isEmpty(childIdArray)) {
            return null;
        }
        List<HosOrganization> hierarchies = this.list(Wrappers.lambdaQuery(HosOrganization.class).in(HosOrganization::getOrgCode, childIdArray));
        if (tree) {
            if (containsCurrent) {
                currentHosOrganizationCode = this.getById(currentHosOrganizationCode).getOrgOrgcCode();
            }
            return buildHosOrganizationTree(hierarchies, currentHosOrganizationCode);
        }
        return hierarchies;
    }

    /**
     * 取指定层级所有下级包
     *
     * @param treeId          code
     * @param containsCurrent 是否包含当前组织
     * @return ID集合
     */
    public List<String> getAllChildrenIdWithParentId(List<HosOrganization> all, String treeId, boolean containsCurrent) {
        List<HosOrganization> childTrees = CollUtil.newArrayList();
        // 递归
        this.treeOrgChildren(childTrees, all, treeId);
        List<String> code = CollUtil.newArrayList();
        for (HosOrganization child : childTrees) {
            code.add(child.getOrgCode());
        }
        if (containsCurrent) {
            code.add(treeId);
        }
        return code;
    }

    /**
     * 获取指定节点的所有子节点
     *
     * @param childTrees     子节点收集器
     * @param trees          当前菜单
     * @param parentTreeCode 父菜单ID
     */
    private void treeOrgChildren(List<HosOrganization> childTrees, List<HosOrganization> trees, String parentTreeCode) {
        for (HosOrganization tree : trees) {
            if (tree.getOrgOrgCode() != null && tree.getOrgOrgCode().equals(parentTreeCode)) {
                treeOrgChildren(childTrees, trees, tree.getOrgCode());
                childTrees.add(tree);
            }
        }
    }


    @Override
    public void updateHosOrganization(HosOrganization hosOrganization) {
        if (ObjectUtil.isEmpty(this.getById(hosOrganization.getOrgCode()))) {
            throw new DataException(StrUtil.format("组织【orgCode = {}】的数据不存在", hosOrganization.getOrgCode()));
        }
        hosOrganization.setOrgLastUpdateUser(SecurityUtils.getLoginName());
        hosOrganization.setOrgLastUpdateDate(DUtil.splitNow()[0]);
        hosOrganization.setOrgLastUpdateTime(DUtil.splitNow()[1]);

        SpringUtil.getBean(CommonDictService.class).saveDictDetail(DictGeneralDTO.build(hosOrganization, HosOrganization.class), "");
    }

    @Override
    public ErrorMsgVO deleteHosOrganization(String orgCode, boolean deleteChild) {
        ErrorMsgVO error = new ErrorMsgVO();
        int delCount = 0;
        List<String> codes = StrUtil.splitTrim(orgCode, StringPool.PIPE);
        if (deleteChild) {
            List<String> codeAll;
            for (String code : codes) {
                // 查询节点下的所有下属节点
                List<HosOrganization> childrenHosOrganization = (List<HosOrganization>) this.findChildrenHosOrganization(code, true, false);
                codeAll = childrenHosOrganization.stream().map(HosOrganization::getOrgCode).collect(Collectors.toList());
                delCount += codeAll.size();
                try {
                    // 先将所有节点的上级节点置为null
                    List<HosOrganization> hosOrganizations = this.listByIds(codeAll);
                    hosOrganizations.forEach(item -> item.setOrgOrgCode(null));
                    this.updateBatchById(hosOrganizations);
                    // 删除所有节点
                    this.removeByIds(codeAll);
                } catch (Exception e) {
                    log.error(ExceptionUtil.getExceptionMessage(e), e);
                    error.setErrorMsg(code, StrUtil.format("组织【code = {}】删除失败, 失败原因【{}】", code, e.getMessage()));
                } finally {
                    codeAll.clear();
                }
            }
        } else {
            delCount = codes.size();
            for (String code : codes) {
                try {
                    // 更新节点
                    List<HosOrganization> child = this.list(Wrappers.lambdaQuery(HosOrganization.class).eq(HosOrganization::getOrgOrgCode, code));
                    HosOrganization byId = this.getById(code);
                    if (CollUtil.isNotEmpty(child)) {
                        List<String> node = child.stream().map(HosOrganization::getOrgCode).collect(Collectors.toList());
                        HosOrganization hosOrganization = new HosOrganization();
                        hosOrganization.setOrgOrgCode(byId.getOrgOrgCode());
                        this.update(hosOrganization, Wrappers.lambdaQuery(HosOrganization.class).in(HosOrganization::getOrgCode, node));
                    }
                    this.removeById(code);
                } catch (Exception e) {
                    log.error(ExceptionUtil.getExceptionMessage(e), e);
                    error.setErrorMsg(code, StrUtil.format("组织【code = {}】删除失败, 失败原因【{}】", code, e.getMessage()));
                }
            }
        }
        error.setCount(delCount);
        return error;
    }

    @Override
    public ErrorMsgVO importHosOrganization(List<HosOrganization> hosOrganizations, boolean updateSupport) {
        ErrorMsgVO error = new ErrorMsgVO();
        if (CollUtil.isEmpty(hosOrganizations)) {
            throw new DataException("导入组织数据不能为空！");
        }
        for (HosOrganization hosOrganization : hosOrganizations) {
            try {
                HosOrganization byId = this.getById(hosOrganization.getOrgCode());
                if (ObjectUtil.isEmpty(byId)) {
                    HosOrganization.setCreateHosOrganization(hosOrganization);
                    SpringUtils.getBean(CommonDictService.class).saveDictDetail(DictGeneralDTO.build(hosOrganization, HosOrganization.class), "");
                } else if (updateSupport) {
                    HosOrganization.setUpdateHosOrganization(hosOrganization);
                    SpringUtils.getBean(CommonDictService.class).saveDictDetail(DictGeneralDTO.build(hosOrganization, HosOrganization.class), "");
                } else {
                    error.setErrorMsg(hosOrganization, StrUtil.format("组织【orgCode = {}】已存在", hosOrganization.getOrgCode()));
                }
            } catch (Exception e) {
                log.error(ExceptionUtil.getExceptionMessage(e), e);
                error.setErrorMsg(hosOrganization, StrUtil.format("组织【orgCode = {}】导入失败, 失败原因【{}】", hosOrganization.getOrgCode(), e.getMessage()));
            }
        }
        error.setCount(hosOrganizations.size());
        return error;
    }
}
